package com.yisin.ssh2;

import java.io.IOException;
import java.util.Hashtable;

import com.yisin.ssh2.jsch.Channel;
import com.yisin.ssh2.jsch.HostKey;
import com.yisin.ssh2.jsch.JSch;
import com.yisin.ssh2.jsch.JSchException;
import com.yisin.ssh2.jsch.Session;
import com.yisin.ssh2.jsch.UIKeyboardInteractive;
import com.yisin.ssh2.jsch.UserInfo;

public abstract class SshBase {
	protected String m_host;
	protected String m_user;
	protected String m_pass;
	protected JSch m_jsch;
	protected Session m_session;
	protected Channel m_channel;

	/**
	 * Default TCP port of SSH protocol
	 */
	private static int SSH_TCP_PORT = 22;

	/**
	 * 
	 * @param sftpHost
	 *            The remote SSH host
	 * @param user
	 *            The login username
	 * @param password
	 *            The login password
	 */
	public SshBase(String sftpHost, String user, String password) {
		this.m_host = sftpHost;
		this.m_user = user;
		this.setPassword(password);
		m_jsch = new JSch();
	}

	// / <summary>
	// / Constructs a new SSH instance
	// / </summary>
	// / <param name="sftpHost">The remote SSH host</param>
	// / <param name="user">The login username</param>
	public SshBase(String sftpHost, String user) {
		this(sftpHost, user, null);
	}

	// / <summary>
	// / Adds identity file for publickey user authentication
	// / </summary>
	// / <param name="privateKeyFile">The path to the private key file</param>
	public void AddIdentityFile(String privateKeyFile) throws JSchException, InstantiationException, IllegalAccessException, ClassNotFoundException {
		m_jsch.addIdentity(privateKeyFile);
	}

	// / <summary>
	// / Adds identity file for publickey user authentication
	// / </summary>
	// / <param name="privateKeyFile">The path to the private key file</param>
	// / <param name="passphrase">A passphrase for decrypting the private key
	// file</param>
	public void AddIdentityFile(String privateKeyFile, String passphrase) throws JSchException, InstantiationException, IllegalAccessException, ClassNotFoundException {
		m_jsch.addIdentity(privateKeyFile, passphrase);
	}

	protected String ChannelType() {
		return "shell";
	}

	// / <summary>
	// / Connect to remote SSH server
	// / </summary>
	public void Connect() throws JSchException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException {
		this.Connect(SSH_TCP_PORT);
	}

	// / <summary>
	// / Connect to remote SSH server
	// / </summary>
	// / <param name="tcpPort">The destination TCP port for this
	// connection</param>
	public void Connect(int tcpPort) throws JSchException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException {
		this.ConnectSession(tcpPort);
		this.ConnectChannel();
	}

	protected void ConnectSession(int tcpPort) throws JSchException, InstantiationException, IllegalAccessException, ClassNotFoundException {
		m_session = m_jsch.getSession(m_user, m_host, tcpPort);
		if (getPassword() != null)
			m_session.setUserInfo(new KeyboardInteractiveUserInfo(getPassword()));
		Hashtable<Object, Object> config = new Hashtable<Object, Object>();
		config.put("StrictHostKeyChecking", "no");
		m_session.setConfig(config);
		m_session.connect();
	}

	protected void ConnectChannel() throws JSchException, IOException {
		m_channel = m_session.openChannel(ChannelType());
		this.OnChannelReceived();
		m_channel.connect();
		this.OnConnected();
	}

	protected void OnConnected() {
	}

	protected void OnChannelReceived() throws IOException {
	}

	// / <summary>
	// / Closes the SSH subsystem
	// / </summary>
	public void Close() throws Exception {
		if (m_channel != null) {
			m_channel.disconnect();
			m_channel = null;
		}
		if (m_session != null) {
			m_session.disconnect();
			m_session = null;
		}
	}

	// / <summary>
	// / Return true if the SSH subsystem is connected
	// / </summary>
	public boolean Connected() {
		if (m_session != null)
			return m_session.isConnected();
		return false;
	}

	// / <summary>
	// / Gets the Cipher algorithm name used in this SSH connection.
	// / </summary>
	public String getCipher() throws Exception {
		checkConnected();
		return m_session.getClientVersion();
	}

	// / <summary>
	// / Gets the server SSH version String.
	// / </summary>
	public String getServerVersion() throws Exception {
		checkConnected();
		return m_session.getServerVersion();
	}

	// / <summary>
	// / Gets the client SSH version String.
	// / </summary>
	public String getClientVersion() throws Exception {
		checkConnected();
		return m_session.getClientVersion();
	}

	public String getHost() throws Exception {
		checkConnected();
		return m_session.getHost();
	}

	public HostKey getHostKey() throws Exception {
		checkConnected();
		return m_session.getHostKey();
	}

	public int getPort() throws Exception {
		checkConnected();
		return m_session.getPort();
	}

	// / <summary>
	// / The password String of the SSH subsystem
	// / </summary>
	public void setPassword(String value) {
		m_pass = value;
	}

	public String getPassword() {
		return m_pass;
	}

	public String Username() {
		return m_user;
	}

	public static Version getVersion() {
		return new Version();
	}

	private void checkConnected() throws Exception {
		if (!Connected()) {
			throw new Exception("SSH session is not connected.");
		}
	}

	// / <summary>
	// / For password and KI auth modes
	// / </summary>
	protected class KeyboardInteractiveUserInfo implements UserInfo, UIKeyboardInteractive {
		String _password;

		public KeyboardInteractiveUserInfo(String password) {
			_password = password;
		}

		public String[] promptKeyboardInteractive(String destination, String name, String instruction, String[] prompt, boolean[] echo) {
			return new String[] { _password };
		}

		public boolean promptYesNo(String message) {
			return true;
		}

		public boolean promptPassword(String message) {
			return true;
		}

		public String getPassword() {
			return _password;
		}

		public boolean promptPassphrase(String message) {
			return true;
		}

		public String getPassphrase() {
			return null;
		}

		public void showMessage(String message) {
		}
	}
}
